package com.foxzzz.osf.fs;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * @Author wujie
 * @Class OsfFile
 * @Description
 * @Date 2020/12/10 11:02
 */
public class OsfPageHolder {
    private final static Long DEFAULT_PAGE_SIZE = 1024 * 8L;
    private final static Long DEFAULT_MAX_FILE_SIZE = 1024 * 1024 * 1024 * 2L;
    private Long maxFileSize;
    private Long maxPageNum;
    private OsfEliminationAlgorithm osfEliminationAlgorithm;
    private Map<Long, OsfPage> cacheMap = new HashMap<>();
    private String path;
    private Long pageSize;
    private RandomAccessFile accessFile;
    private FileChannel fileChannel;


    public OsfPageHolder(String path) {
        this(path, DEFAULT_PAGE_SIZE, DEFAULT_MAX_FILE_SIZE);
    }

    public OsfPageHolder(String path, long pageSize, long maxFileSize) {
        this.path = path;
        this.pageSize = pageSize;
        this.maxFileSize = maxFileSize;
        maxPageNum = this.maxFileSize / pageSize;
    }


    public void setEliminationAlgorithm(OsfEliminationAlgorithm algorithm) {
        this.osfEliminationAlgorithm = algorithm;
    }

    private void checkNum(long num) {
        if (num >= maxPageNum) {
            throw new RuntimeException("页码超过上限 " + maxPageNum);
        }
    }

    private OsfPage getPageFromFile(long num) {
        try {
            if (accessFile == null) {
                accessFile = new RandomAccessFile(path, "rw");
            }
            if (fileChannel == null) {
                fileChannel = accessFile.getChannel();
            }
            long start = num * pageSize;
            return new OsfPage(fileChannel, num, start, start + pageSize);
        } catch (Exception e) {
            throw new RuntimeException("从文件中获取页出现错误", e.getCause());
        }
    }


    public OsfPage getPage(long pageNum) {
        checkNum(pageNum);
        Boolean cacheAble = Optional.ofNullable(osfEliminationAlgorithm).map(o -> o.use(pageNum)).orElse(true);
        OsfPage osfPage = cacheMap.get(pageNum);
        if (osfPage == null) {
            osfPage = getPageFromFile(pageNum);
            if (cacheAble) {
                cacheMap.put(pageNum, osfPage);
            }
        }
        return osfPage;
    }

    public void close() {
        if (fileChannel != null) {
            try {
                fileChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (accessFile != null) {
            try {
                accessFile.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
